home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / period.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  31KB  |  1,037 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) period.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: period.c,v 1.2 88/07/15 14:51:00 shebs Exp $ */
  8.  
  9. /* This file defines a mini-postfix language for use in specifying periods. */
  10. /* The language is very restrictive, both lexically and semantically.  The */
  11. /* only kinds of objects are integers, strings, symbols, and vectors, while */
  12. /* the only way to get new words is to make new unit, resource, and terrain */
  13. /* types, or to create names for constants.  There is a strong flavor of APL */
  14. /* in the kinds of vector operations available. */
  15.  
  16. /* Stack hacking is a little dubious - we pop the stack *before* doing the */
  17. /* word, but don't copy args.  Potential for disaster if word pushes */
  18. /* results too soon... Should use pointers to objects also, for portability. */
  19.  
  20. #include "config.h"
  21. #include "misc.h"
  22. #include "period.h"
  23. #include "unit.h"
  24.  
  25. /* Cute little macro from the MG people.  It computes an offset of the slot */
  26. /* "m" from the beginning of structure type "t". */
  27.  
  28. #define OFFSET(t,m) ((char *) &(((t *) 0)->m) - ((char *) ((t *) 0)))
  29. #define VOFFSET(t,m) ((char *) (((t *) 0)->m) - ((char *) ((t *) 0)))
  30.  
  31. /* The "dictionary" is a symbol table, thus needs to be large enough to hold */
  32. /* all pre-defined symbols, as well as those added during execution. */
  33. /* There are about 200 predefined words. */
  34.  
  35. #define DICTSIZE 400
  36.  
  37. /* The stack doesn't have to be very deep, unless the period description is */
  38. /* really perverted.  Depth should be at least MAXUTYPES+5 or so... */
  39.  
  40. #define STACKSIZE 50
  41.  
  42. /* An object has four possible types.  Numbers can only be integers, and */
  43. /* vectors may only contain integers.  Strings are unlimited, while symbols */
  44. /* mustn't overflow the dictionary. */
  45.  
  46. #define INT 0
  47. #define SYM 1
  48. #define STR 2
  49. #define VEC 3
  50.  
  51. /* States of tokenizer (along with symbols just above). */
  52.  
  53. #define ENDTOKEN (-99)
  54. #define NIL (-1)
  55.  
  56. /* Types of words (avoids using C hook functions usually). */
  57.  
  58. #define FN 0            /* arbitrary function */
  59. #define U1 1                    /* put number into utype slot */
  60. #define S1 2                    /* put string into utype slot */
  61. #define U2 3                    /* put number(s) into utype array slot */
  62. #define VV 5                    /* push value onto stack */
  63. #define P0 6                    /* put number into period structure */
  64. #define T1 7                    /* put number into terrain type slot */
  65.  
  66. /* Note that this declaration results in largish objects, but anything more */
  67. /* clever is probably overkill.  */
  68.  
  69. /* MAXUTYPES must be larger than MAX[RT]TYPES (obscure). */
  70.  
  71. typedef struct a_obj {
  72.     int type;          /* type of the object */
  73.     int len;           /* length if it's a vector */
  74.     union {
  75.     int num;       /* numeric value */
  76.     char *str;     /* string value */
  77.     int sym;       /* symbol value (index into dictionary) */
  78.     short vec[MAXUTYPES];  /* vector value */
  79.     } v;               /* value is pretty sizeable because of vector values */
  80. } Obj;
  81.  
  82. /* The dictionary interfaces words to C functions. */
  83.  
  84. typedef struct dict {
  85.     char *name;        /* a word of the language */
  86.     int wtype;         /* type of word (1-d set, string set, etc) */
  87.     int nargs;         /* how much to pull from the stack */
  88.     union {
  89.         int (*code)(); /* pointer to a C function */
  90.         int offset;    /* for offsets into structures */
  91.         Obj value;     /* a "default" value used sometimes */
  92.     } f;
  93. } Dict;
  94.  
  95. Dict words[DICTSIZE];  /* the dictionary itself */
  96.  
  97. Obj stack[STACKSIZE];  /* the evaluation stack */
  98. Obj curobj;           /* the current word (used in weird way) */
  99.  
  100. FILE *pfp;             /* period file pointer */
  101.  
  102. bool mustbenew = TRUE;  /* flag for an efficiency hack during init */
  103.  
  104. int numwords;          /* number of words presently in dictionary */
  105. int top = 0;           /* index to the top of the stack */
  106.  
  107. int push_value();      /* predeclare to cover some forward refs */
  108.  
  109. /* Read a period section of an already-opened mapfile. */
  110. /* The debug prints are very important for debugging period files - they */
  111. /* provide at least a simple test that things are being read correctly. */
  112. /* There are also a number of consistency checks, though not all possible, */
  113. /* and nothing is done to test play balance for the period :-). */
  114.  
  115. read_period(fp)
  116. FILE *fp;
  117. {
  118.     int extension, endsym, si;
  119.  
  120.     pfp = fp;
  121.     fscanf(pfp, "Period %d\n", &extension);
  122.     if (Debug) printf("Trying to read period ...\n");
  123.     init_words();
  124.     endsym = lookup_symbol("end");
  125.     clear_period();
  126.     while (get_token(pfp)) {
  127.     if (Debug) {
  128.         print_object(curobj, FALSE);
  129.     }
  130.     if (curobj.type == SYM) {
  131.         si = curobj.v.sym;
  132.         if (si == endsym) break;
  133.         if (top < words[si].nargs) {
  134.         fprintf(stderr, "Stack underflow!\n");
  135.         exit(1);
  136.         }
  137.         top -= words[si].nargs;
  138.         switch (words[si].wtype) {
  139.         case FN:
  140.         execute_word(words[si].f.code, words[si].nargs);
  141.         break;
  142.         case P0:
  143.         set_0d_n(stack[top], words[si].f.offset);
  144.         break;
  145.         case U1:
  146.         set_1d_n(stack[top], stack[top+1], words[si].f.offset);
  147.         break;
  148.         case S1:
  149.         set_1d_s(stack[top], stack[top+1], words[si].f.offset);
  150.         break;
  151.         case U2:
  152.         set_2d(stack[top], stack[top+1], stack[top+2],
  153.                words[si].f.offset);
  154.         break;
  155.         case VV:
  156.         push_value();
  157.         break;
  158.         case T1:
  159.         set_1d_t(stack[top], stack[top+1], words[si].f.offset);
  160.         break;
  161.         default:
  162.             case_panic("word type", words[si].wtype);
  163.         break;
  164.         }
  165.     } else {
  166.         push_word();
  167.     }
  168.     }
  169.     check_period_numbers();
  170.     if (Debug) printf("... Done reading period.\n");
  171. }
  172.  
  173. /* Execution is straightforward, but we need to know how many arguments each */
  174. /* C function expects. */
  175.  
  176. execute_word(fn, args)
  177. int (*fn)(), args;
  178. {
  179.     if (fn == NULL) abort();
  180.     switch (args) {
  181.     case 0:
  182.     (*fn)();
  183.     break;
  184.     case 1:
  185.     (*fn)(stack[top]);
  186.     break;
  187.     case 2:
  188.     (*fn)(stack[top], stack[top+1]);
  189.     break;
  190.     case 3:
  191.     (*fn)(stack[top], stack[top+1], stack[top+2]);
  192.     break;
  193.     case 4:
  194.     (*fn)(stack[top], stack[top+1], stack[top+2], stack[top+3]);
  195.     break;
  196.     default:
  197.         case_panic("argument", args);
  198.     break;
  199.     }
  200. }
  201.  
  202. /* Alas, the tokenizer is too simple to bother with lex, and does too much */
  203. /* to be a neat and short bit of code.  The states are NIL, INT, STR, SYM, */
  204. /* and ENDTOKEN.  The process starts in NIL, and succeeds in the state */
  205. /* ENDTOKEN, but fails (returning flag) if there is any sort of error. */
  206. /* Basically each token type is flagged by its special character(s), and */
  207. /* is terminated by whitespace (i.e. state machine is a bunch of looping */
  208. /* states with only other transitions to and from NIL). */
  209.  
  210. get_token(fp)
  211. FILE *fp;
  212. {
  213.     bool minus;
  214.     char ch, str[BUFSIZE];
  215.     int state = NIL, j = 0;
  216.  
  217.     while (state != ENDTOKEN) {
  218.     ch = getc(fp);
  219.     switch (ch) {
  220.     case EOF:
  221.         return FALSE;
  222.     case ';':
  223.         while ((ch = getc(fp)) != '\n' && ch != EOF);
  224.         break;
  225.     default:
  226.         switch (state) {
  227.         case NIL:
  228.         if (isdigit(ch) || ch == '-') {
  229.             minus = (ch == '-');
  230.             curobj.v.num = (minus ? 0 : ch - '0');
  231.             state = curobj.type = INT;
  232.         } else if (iswhite(ch)) {
  233.             /* nothing to do here */
  234.         } else if (ch == '"') {
  235.             state = curobj.type = STR;
  236.         } else {
  237.             str[j++] = ch;
  238.             state = curobj.type = SYM;
  239.         }
  240.         break;
  241.         case INT:
  242.         if (isdigit(ch)) {
  243.             curobj.v.num = curobj.v.num * 10 + ch - '0';
  244.         } else {
  245.             if (minus) curobj.v.num = 0 - curobj.v.num;
  246.             state = ENDTOKEN;
  247.         }
  248.         break;
  249.         case STR:
  250.         if (ch == '"') {
  251.             str[j] = '\0';
  252.             curobj.v.str = copy_string(str);
  253.             state = ENDTOKEN;
  254.         } else {
  255.             str[j++] = ch;
  256.         }
  257.         break;
  258.         case SYM:
  259.         if (iswhite(ch)) {
  260.             str[j] = '\0';
  261.             curobj.v.sym = lookup_symbol(str);
  262.             if (curobj.v.sym < 0) {
  263.             fprintf(stderr, "Undefined symbol \"%s\"!\n", str);
  264.             exit(1);
  265.             }
  266.             state = ENDTOKEN;
  267.         } else {
  268.             str[j++] = ch;
  269.         }
  270.         break;
  271.         default:
  272.         case_panic("token state", state);
  273.         break;
  274.         }
  275.         break;
  276.     }
  277.     }
  278.     return TRUE;
  279. }
  280.  
  281. /* The dictionary starts out empty.  During initialization before reading, */
  282. /* the basic set of predefined words come in.  Then units and resources get */
  283. /* words later on as requested.  The "mustbenew" flag enables/disables redef */
  284. /* checks.  Return address of new word. as a convenience. */
  285.  
  286. add_word(word, type, args, fn)
  287. char *word;
  288. int type, args;
  289. int (*fn)();
  290. {
  291.     int sym;
  292.  
  293.     if (mustbenew && (sym = lookup_symbol(word)) > 0) {
  294.     fprintf(stderr, "May not redefine \"%s\"!\n", word);
  295.     return sym;
  296.     }
  297.     if (numwords < DICTSIZE) {
  298.     words[numwords].name = word;
  299.     words[numwords].wtype = type;
  300.     words[numwords].nargs = args;
  301.     words[numwords].f.code = fn;
  302.     numwords++;
  303.     return (numwords - 1);
  304.     } else {
  305.     fprintf(stderr, "Dictionary is full!\n");
  306.     exit(1);
  307.     }
  308. }
  309.  
  310. /* Look up a symbol in the (not so small) dictionary.  DO NOT add it if not */
  311. /* found - don't want any screwups to be compounded by feeble DWIM efforts. */
  312.  
  313. lookup_symbol(str)
  314. char *str;
  315. {
  316.     int i;
  317.  
  318.     for (i = 0; i < numwords; ++i) {
  319.     if (strcmp(words[i].name, str) == 0) return i;
  320.     }
  321.     return (-1);
  322. }
  323.  
  324. /* Put the current word on the stack, checking for overflow first. */
  325.  
  326. push_word()
  327. {
  328.     if (top < STACKSIZE) {
  329.     stack[top++] = curobj;
  330.     } else {
  331.     fprintf(stderr, "Stack overflow!\n");
  332.     exit(1);
  333.     }
  334. }
  335.  
  336. /* Push the "value" slot of the word being executed onto the stack. */
  337. /* Used by all names and characters of units and resources - necessary */
  338. /* because we can't define individual distinct C codes for each type. */
  339.  
  340. push_value()
  341. {
  342.     if (top < STACKSIZE) {
  343.     stack[top++] = words[curobj.v.sym].f.value;
  344.     } else {
  345.     print_stack();
  346.     fprintf(stderr, "Stack overflow!\n");
  347.     exit(1);
  348.     }
  349. }
  350.  
  351. /* Generic number pusher. */
  352.  
  353. push_number(n)
  354. int n;
  355. {
  356.     if (top < STACKSIZE) {
  357.     stack[top].type = INT;
  358.     stack[top].v.num = n;
  359.     ++top;
  360.     } else {
  361.     fprintf(stderr, "Stack overflow!\n");
  362.     exit(1);
  363.     }
  364. }
  365.  
  366. /* Fill in some very basic numbers at the outset. This is essentially a */
  367. /* "clear" operation, rampaging straight through the three period structures */
  368. /* then filling in a few numbers that *must* have reasonable values for the */
  369. /* program to work.  A couple are convenience values, such as "nukehit". */
  370.  
  371. clear_period()
  372. {
  373.     char *base;
  374.     int i, u, u2, r, t, tmp1, tmp2;
  375.  
  376.     tmp1 = period.numsnames;  tmp2 = period.numunames;
  377.     base = (char *) .
  378.     for (i = 0; i < sizeof(Period); ++i) base[i] = '\0';
  379.     base = (char *) utypes;
  380.     for (i = 0; i < sizeof(Utype) * MAXUTYPES; ++i) base[i] = '\0';
  381.     base = (char *) rtypes;
  382.     for (i = 0; i < sizeof(Rtype) * MAXRTYPES; ++i) base[i] = '\0';
  383.     base = (char *) ttypes;
  384.     for (i = 0; i < sizeof(Ttype) * MAXTTYPES; ++i) base[i] = '\0';
  385.     period.numsnames = tmp1;  period.numunames = tmp2;
  386.     period.name = "unspecified";
  387.     period.fontname = "";
  388.     period.scale = 100;
  389.     period.nukehit = 50;
  390.     period.countrysize = 3;
  391.     period.mindistance = 7;
  392.     period.maxdistance = 60;
  393.     period.firstutype = period.firstptype = NOTHING;
  394.     period.counterattack = TRUE;
  395.     period.defaultterrain = period.edgeterrain = -1;
  396.     period.altroughness = 80;  period.wetroughness = 70;
  397.     period.spychance = 1;  period.spyquality = 50;
  398.     for (u = 0; u < MAXUTYPES; ++u) {
  399.     utypes[u].attdamage = 1;
  400.     utypes[u].attritionmsg = "suffers attrition";
  401.     utypes[u].accidentmsg = "has an accident";
  402.     utypes[u].starvemsg = "runs out of supplies and dies";
  403.     utypes[u].destroymsg = "destroys";
  404.     utypes[u].visibility = 100;
  405.     utypes[u].seebest  = 100;
  406.     utypes[u].seeworst = 100;
  407.     utypes[u].seerange = 1;
  408.     utypes[u].hp = 1;
  409.     utypes[u].counterable = TRUE;
  410.     utypes[u].control = 100;
  411.     for (u2 = 0; u2 < MAXUTYPES; ++u2) {
  412.         utypes[u].entertime[u2] = 1;
  413.         utypes[u].mobility[u2] = 100;
  414.     }
  415.     for (r = 0; r < MAXRTYPES; ++r) {
  416.         utypes[u].stockpile[r] = 100;
  417.     }
  418.     for (t = 0; t < MAXTTYPES; ++t) {
  419.         utypes[u].moves[t] = -1;
  420.     }
  421.     }
  422.     for (t = 0; t < MAXTTYPES; ++t) {
  423.     ttypes[t].minalt = ttypes[t].minwet = 0;
  424.     ttypes[t].maxalt = ttypes[t].maxwet = 100;
  425.     }
  426. }
  427.  
  428. /* Random side and unit names must be cleared separately by explicit cmd, */
  429. /* since periods don't always want to define their own. */
  430.  
  431. clear_side_names() {  period.numsnames = 0;  }
  432.  
  433. clear_unit_names() {  period.numunames = 0;  }
  434.  
  435. /* Words that set the contents of arrays are polymorphic in that they can */
  436. /* do array-like assignments as well as single value assignment. */
  437.  
  438. set_0d_n(n, offset)
  439. Obj n;
  440. int offset;
  441. {
  442.     *((short *) ((char *) ((char *) &period) + offset)) = n.v.num;
  443. }
  444.  
  445. /* No string vectors, so all we can do is to assign the same string to */
  446. /* one or more different array positions. */
  447.  
  448. #define s1ds(s,i) (*((char **)((char *)((char *)&(utypes[i])) + offset)) = s)
  449.  
  450. set_1d_s(s, i, offset)
  451. Obj s, i;
  452. int offset;
  453. {
  454.     int z;
  455.  
  456.     if (s.type == STR) {
  457.     if (i.type == INT) {
  458.         s1ds(s.v.str, i.v.num);
  459.     } else {
  460.         for (z = 0; z < i.len; ++z)    s1ds(s.v.str, i.v.vec[z]);
  461.     }
  462.     } else {
  463.     arg_error_1d("1st arg must be string", s, i);
  464.     }
  465. }
  466.  
  467. /* One-dimensional arrays allow the combinations of scalar value & index SS, */
  468. /* scalar value & vector index SV, and two vectors VV.  The 4th combo is not */
  469. /* sensible. */
  470.  
  471. #define s1d(n,i) (*((short *)((char *)((char *)&(utypes[i])) + offset)) = n)
  472.  
  473. set_1d_n(n, i, offset)
  474. Obj n, i;
  475. int offset;
  476. {
  477.     int z;
  478.  
  479.     if (n.type == INT) {
  480.     if (i.type == INT) {
  481.         s1d(n.v.num, i.v.num);
  482.     } else {
  483.         for (z = 0; z < i.len; ++z)    s1d(n.v.num, i.v.vec[z]);
  484.     }
  485.     } else {
  486.     if (i.type == INT) {
  487.         arg_error_1d("mismatched arguments", n, i);
  488.     } else {
  489.         for (z = 0; z < n.len; ++z) s1d(n.v.vec[z], i.v.vec[z]);
  490.     }
  491.     }
  492. }
  493.  
  494. /* This is identical to set_1d_n, but puts stuff into ttypes array instead. */
  495.  
  496. #define s1dt(n,i) (*((short *)((char *)((char *)&(ttypes[i])) + offset)) = n)
  497.  
  498. set_1d_t(n, i, offset)
  499. Obj n, i;
  500. int offset;
  501. {
  502.     int z;
  503.  
  504.     if (n.type == INT) {
  505.     if (i.type == INT) {
  506.         s1dt(n.v.num, i.v.num);
  507.     } else {
  508.         for (z = 0; z < i.len; ++z)    s1dt(n.v.num, i.v.vec[z]);
  509.     }
  510.     } else {
  511.     if (i.type == INT) {
  512.         arg_error_1d("mismatched arguments", n, i);
  513.     } else {
  514.         for (z = 0; z < n.len; ++z) s1dt(n.v.vec[z], i.v.vec[z]);
  515.     }
  516.     }
  517. }
  518.  
  519. /* Dump the offending values and leave - any attempt to keep going will */
  520. /* probably screw things up and maybe cause a core dump. */
  521.  
  522. arg_error_1d(msg, a, b)
  523. char *msg;
  524. Obj a, b;
  525. {
  526.     fprintf(stderr, "Error in array setting: %s\n", msg);
  527.     print_object(a, TRUE);
  528.     print_object(b, TRUE);
  529.     exit(1);
  530. }
  531.  
  532. /* Two-dimensional setting is more complicated.  VSS is not useful, and VVV */
  533. /* can only set diagonals.  All others are useful. */
  534.  
  535. #define s2d(n,i,j) (*((short *)((char *)((char *)&(utypes[j]))+offset+2*i))=n)
  536.  
  537. set_2d(n, i1, i2, offset)
  538. Obj n, i1, i2;
  539. int offset;
  540. {
  541.     int z, zz;
  542.  
  543.     if (n.type == INT) {
  544.     if (i1.type == INT) {
  545.         if (i2.type == INT) {
  546.         s2d(n.v.num, i1.v.num, i2.v.num);
  547.         } else {
  548.         for (z = 0; z < i2.len; ++z) {
  549.             s2d(n.v.num, i1.v.num, i2.v.vec[z]);
  550.         }
  551.         }
  552.     } else {
  553.         if (i2.type == INT) {
  554.         for (z = 0; z < i1.len; ++z) {
  555.             s2d(n.v.num, i1.v.vec[z], i2.v.num);
  556.         }
  557.         } else {
  558.         for (z = 0; z < i1.len; ++z) {
  559.             for (zz = 0; zz < i2.len; ++zz) {
  560.             s2d(n.v.num, i1.v.vec[z], i2.v.vec[zz]);
  561.             }
  562.         }
  563.         }
  564.     }
  565.     } else {
  566.     if (i1.type == INT) {
  567.         if (i2.type == INT) {
  568.         arg_error_2d("mismatched argument types", n, i1, i2);
  569.         } else {
  570.         if (n.len != i2.len)
  571.             arg_error_2d("mismatched vectors", n, i1, i2);
  572.         for (z = 0; z < i2.len; ++z) {
  573.             s2d(n.v.vec[z], i1.v.num, i2.v.vec[z]);
  574.         }
  575.         }
  576.     } else {
  577.         if (i2.type == INT) {
  578.         if (n.len != i1.len)
  579.             arg_error_2d("mismatched vectors", n, i1, i2);
  580.         for (z = 0; z < i1.len; ++z) {
  581.             s2d(n.v.vec[z], i1.v.vec[z], i2.v.num);
  582.         }
  583.         } else {
  584.         arg_error_2d("mismatched argument types", n, i1, i2);
  585.         }
  586.     }
  587.     }
  588. }
  589.  
  590. /* Dump the offending values and die. */
  591.  
  592. arg_error_2d(msg, a, b, c)
  593. char *msg;
  594. Obj a, b, c;
  595. {
  596.     fprintf(stderr, "Error in array setting: %s\n", msg);
  597.     print_object(a, TRUE);
  598.     print_object(b, TRUE);
  599.     print_object(c, TRUE);
  600.     exit(1);
  601. }
  602.  
  603. /* Fortunately there are no 3D arrays!! */
  604.  
  605. /* The dreary wasteland of implementations of words. */
  606. /* Fortunately, most of them can use the multi-dimensional array hacking as */
  607. /* defined already, but we have to have both a "setter" function and a */
  608. /* "dictionary" function, so lots of short functions :-(. */
  609.  
  610. /* Build an arbitrary integer vector using [ ] words. */
  611.  
  612. start_vector()
  613. {
  614.     stack[top].type = STR;
  615.     stack[top++].v.str = "start-vector";  /* check overflow? */
  616. }
  617.  
  618. /* Vector creation is a little tricky, since we usually take a variable */
  619. /* number of words from the stack.  The basic scheme scans the stack for */
  620. /* for a non-integer or stack bottom, then builds a vector at that point. */
  621.  
  622. finish_vector()
  623. {
  624.     int i, bot, size;
  625.  
  626.     for (i = top-1; i >= 0; --i) {
  627.     if (stack[i].type != INT) {
  628.         bot = i;
  629.         break;
  630.     }
  631.     }
  632.     size = top - bot - 1;
  633.     if (size <= MAXUTYPES) {
  634.     stack[bot].type = VEC;
  635.     stack[bot].len = size;
  636.     for (i = 0; i < size; ++i) {
  637.         stack[bot].v.vec[i] = stack[bot+i+1].v.num;
  638.     }
  639.     top = bot + 1;
  640.     } else {
  641.     fprintf(stderr, "Vector too big!\n");
  642.     exit(1);
  643.     }
  644. }
  645.  
  646. /* Assign a value to a variable, basically.  Actually, we're defining a new */
  647. /* word and filling its value slot. */
  648.  
  649. set_define(a1, a2)
  650. Obj a1, a2;
  651. {
  652.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = a1;
  653. }
  654.  
  655. push_true() { push_number(TRUE); }
  656.  
  657. push_false() { push_number(FALSE); }
  658.  
  659. push_nothing() { push_number(NOTHING); }
  660.  
  661. /* Things that don't fit in with all the "short" slots. */
  662.  
  663. set_periodname(a1) Obj a1; { period.name = a1.v.str; }
  664.  
  665. set_fontname(a1) Obj a1; { period.fontname = a1.v.str; }
  666.  
  667. /* Add a new type of unit.  All args must be strings, but after the defn, */
  668. /* both char and full name may be used as normal words. */
  669.  
  670. define_utype(a1, a2, a3)
  671. Obj a1, a2, a3;
  672. {
  673.     Obj obj;
  674.  
  675.     if (period.numutypes < MAXUTYPES) {
  676.     obj.type = INT;
  677.     obj.v.num = period.numutypes;
  678.     utypes[period.numutypes].uchar = a1.v.str[0];
  679.     utypes[period.numutypes].name  = a2.v.str;
  680.     utypes[period.numutypes].help  = a3.v.str;
  681.     words[add_word(a1.v.str, VV, 0, NULL)].f.value = obj;
  682.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = obj;
  683.     period.numutypes++;
  684.     } else {
  685.     fprintf(stderr, "Limited to %d types of units! (failed on %s)\n",
  686.         MAXUTYPES, a2.v.str);
  687.     exit(1);
  688.     }
  689. }
  690.  
  691. /* Add a new type of resource (similar to adding unit type). */
  692.  
  693. define_rtype(a1, a2, a3)
  694. Obj a1, a2, a3;
  695. {
  696.     Obj obj;
  697.  
  698.     if (period.numrtypes < MAXRTYPES) {
  699.     obj.type = INT;
  700.     obj.v.num = period.numrtypes;
  701.     rtypes[period.numrtypes].rchar = a1.v.str[0];
  702.     rtypes[period.numrtypes].name  = a2.v.str;
  703.     rtypes[period.numrtypes].help  = a3.v.str;
  704.     words[add_word(a1.v.str, VV, 0, NULL)].f.value = obj;
  705.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = obj;
  706.     period.numrtypes++;
  707.     } else {
  708.     fprintf(stderr, "Limited to %d types of resources! (failed on %s)\n",
  709.         MAXRTYPES, a2.v.str);
  710.     exit(1);
  711.     }
  712. }
  713.  
  714. /* Add a new type of terrain.  All args must be strings, but after the defn, */
  715. /* only full name may be used as a normal word. (Since most terrain chars */
  716. /* seem to conflict with various important chars in this language.) */
  717.  
  718. define_ttype(a1, a2, a3)
  719. Obj a1, a2, a3;
  720. {
  721.     Obj obj;
  722.  
  723.     if (period.numttypes < MAXTTYPES) {
  724.     obj.type = INT;
  725.     obj.v.num = period.numttypes;
  726.     ttypes[period.numttypes].tchar = a1.v.str[0];
  727.     ttypes[period.numttypes].name  = a2.v.str;
  728.     ttypes[period.numttypes].color = a3.v.str;
  729.     /* Terrain chars are weird, so don't add as new words */
  730.     words[add_word(a2.v.str, VV, 0, NULL)].f.value = obj;
  731.     period.numttypes++;
  732.     } else {
  733.     fprintf(stderr, "Limited to %d types of terrain! (failed on %s)\n",
  734.         MAXTTYPES, a2.v.str);
  735.     exit(1);
  736.     }
  737. }
  738.  
  739. /* Push vector of all types of objects for various classes. */
  740.  
  741. push_u_vec() { push_iota_vec(period.numutypes); }
  742.  
  743. push_r_vec() { push_iota_vec(period.numrtypes); }
  744.  
  745. push_t_vec() { push_iota_vec(period.numttypes); }
  746.  
  747. /* Simulate the "iota" operator of APL; push a vector of consecutive ints. */
  748.  
  749. push_iota_vec(n)
  750. int n;
  751. {
  752.     int i;
  753.  
  754.     stack[top].type = VEC;
  755.     stack[top].len = n;
  756.     for (i = 0; i < n; ++i) stack[top].v.vec[i] = i;
  757.     ++top;
  758. }
  759.  
  760. /* Add the string into the array of random side names. */
  761.  
  762. add_side_name(a1)
  763. Obj a1;
  764. {
  765.     snames[period.numsnames++] = a1.v.str;
  766. }
  767.  
  768. /* Add the string into the array of random unit names. */
  769.  
  770. add_unit_name(a1)
  771. Obj a1;
  772. {
  773.     unames[period.numunames++] = a1.v.str;
  774. }
  775.  
  776. /* Absorb designer notes directly instead of trying to interpret words or */
  777. /* strings or whatever.  This version limits commentary to about 8 pages, */
  778. /* which should be more than enough. */
  779.  
  780. begin_notes()
  781. {
  782.     char *line;
  783.     int i = 0;
  784.  
  785.     period.notes = (char **) malloc(400 * sizeof(char *));
  786.     while ((line = read_line(pfp)) != NULL) {
  787.     if (strcmp(line, "end{notes}") == 0) break;
  788.     period.notes[i++] = line;
  789.     if (i >= 399) break;
  790.     }
  791.     period.notes[i] = NULL;
  792. }
  793.  
  794. /* Print the whole stack, attempting to fit it all on one line (hah!). */
  795.  
  796. print_stack()
  797. {
  798.     int i;
  799.  
  800.     if (top == 0) {
  801.     printf("/* Stack is empty. */\n");
  802.     } else {
  803.     printf("/* Stack ");
  804.     for (i = 0; i < top; ++i) {
  805.         print_object(stack[i], FALSE);
  806.     }
  807.     printf("*/\n");
  808.     }
  809. }
  810.  
  811. /* Ah, the joys of polymorphism.  Gotta print each type out differently. */
  812. /* I suppose it's better than not knowing what the types of things are... */
  813.  
  814. print_object(obj, newline)
  815. Obj obj;
  816. bool newline;
  817. {
  818.     int i;
  819.  
  820.     switch (obj.type) {
  821.     case INT:
  822.     printf("%d ", obj.v.num);
  823.     break;
  824.     case STR:
  825.     printf("\"%s\" ", obj.v.str);
  826.     break;
  827.     case SYM:
  828.     printf("%s ", words[obj.v.sym].name);
  829.     break;
  830.     case VEC:
  831.     printf("[");
  832.     for (i = 0; i < obj.len; ++i) printf("%d,", obj.v.vec[i]);
  833.     printf("] ");
  834.     break;
  835.     }
  836.     if (newline) printf("\n");
  837. }
  838.  
  839. /* Build the initial dictionary.  At the end of this file, to eliminate need */
  840. /* for a zillion function declarations! */
  841.  
  842. init_words()
  843. {
  844.     numwords = 0;
  845.     mustbenew = FALSE;   /* efficiency hack */
  846.     add_word("[", FN, 0, start_vector);
  847.     add_word("]", FN, 0, finish_vector);
  848.     add_word("true", FN, 0, push_true);
  849.     add_word("false", FN, 0, push_false);
  850.     add_word("define", FN, 2, set_define);
  851.     add_word("period-name", FN, 1, set_periodname);
  852.     add_word("font-name", FN, 1, set_fontname);
  853.     add_word("scale", P0, 1, OFFSET(Period, scale));
  854.     add_word("utype", FN, 3, define_utype);
  855.     add_word("rtype", FN, 3, define_rtype);
  856.     add_word("ttype", FN, 3, define_ttype);
  857.     add_word("u*", FN, 0, push_u_vec);
  858.     add_word("r*", FN, 0, push_r_vec);
  859.     add_word("t*", FN, 0, push_t_vec);
  860.     add_word("nothing", FN, 0, push_nothing);
  861.     add_word("dark", T1, 2, OFFSET(Ttype, dark));
  862.     add_word("nuked", T1, 2, OFFSET(Ttype, nuked));
  863.     add_word("max-alt", T1, 2, OFFSET(Ttype, maxalt));
  864.     add_word("min-alt", T1, 2, OFFSET(Ttype, minalt));
  865.     add_word("max-wet", T1, 2, OFFSET(Ttype, maxwet));
  866.     add_word("min-wet", T1, 2, OFFSET(Ttype, minwet));
  867.     add_word("inhabitants", T1, 2, OFFSET(Ttype, inhabitants));
  868.     add_word("independence", T1, 2, OFFSET(Ttype, independence));
  869.     add_word("default-terrain", P0, 1, OFFSET(Period, defaultterrain));
  870.     add_word("edge-terrain", P0, 1, OFFSET(Period, edgeterrain));
  871.     add_word("alt-roughness", P0, 1, OFFSET(Period, altroughness));
  872.     add_word("wet-roughness", P0, 1, OFFSET(Period, wetroughness));
  873.     add_word("icon-name", S1, 2, OFFSET(Utype, bitmapname));
  874.     add_word("neutral", U1, 2, OFFSET(Utype, isneutral));
  875.     add_word("territory", U1, 2, OFFSET(Utype, territory));
  876.     add_word("can-disband", U1, 2, OFFSET(Utype, disband));
  877.     add_word("efficiency", P0, 1, OFFSET(Period, efficiency));
  878.     add_word("population", P0, 1, OFFSET(Period, population));
  879.     add_word("hostility", P0, 1, OFFSET(Period, hostility));
  880.     add_word("max-quality", U1, 2, OFFSET(Utype, maxquality));
  881.     add_word("max-morale", U1, 2, OFFSET(Utype, maxmorale));
  882.     add_word("control", U1, 2, OFFSET(Utype, control));
  883.     add_word("country-size", P0, 1, OFFSET(Period, countrysize));
  884.     add_word("country-min-distance", P0, 1, OFFSET(Period, mindistance));
  885.     add_word("country-max-distance", P0, 1, OFFSET(Period, maxdistance));
  886.     add_word("first-unit", P0, 1, OFFSET(Period, firstutype));
  887.     add_word("first-product", P0, 1, OFFSET(Period, firstptype));
  888.     add_word("in-country", U1, 2, OFFSET(Utype, incountry));
  889.     add_word("density", U1, 2, OFFSET(Utype, density));
  890.     add_word("named", U1, 2, OFFSET(Utype, named));
  891.     add_word("already-seen", U1, 2, OFFSET(Utype, alreadyseen));
  892.     add_word("favored", U2, 3, VOFFSET(Utype, favored));
  893.     add_word("stockpile", U2, 3, VOFFSET(Utype, stockpile));
  894.     add_word("known-radius", P0, 1, OFFSET(Period, knownradius));
  895.     add_word("spy-chance", P0, 1, OFFSET(Period, spychance));
  896.     add_word("spy-quality", P0, 1, OFFSET(Period, spyquality));
  897.     add_word("revolt", U1, 2, OFFSET(Utype, revolt));
  898.     add_word("surrender", U1, 2, OFFSET(Utype, surrender));
  899.     add_word("siege", U1, 2, OFFSET(Utype, siege));
  900.     add_word("attrition", U2, 3, VOFFSET(Utype, attrition));
  901.     add_word("attrition-damage", U1, 2, OFFSET(Utype, attdamage));
  902.     add_word("attrition-message", S1, 2, OFFSET(Utype, attritionmsg));
  903.     add_word("accident", U2, 3, VOFFSET(Utype, accident));
  904.     add_word("accident-message", S1, 2, OFFSET(Utype, accidentmsg));
  905.     add_word("make", U2, 3, VOFFSET(Utype, make));
  906.     add_word("maker", U1, 2, OFFSET(Utype, maker));
  907.     add_word("startup", U1, 2, OFFSET(Utype, startup));
  908.     add_word("research", U1, 2, OFFSET(Utype, research));
  909.     add_word("to-make", U2, 3, VOFFSET(Utype, tomake));
  910.     add_word("repair", U2, 3, VOFFSET(Utype, repair));
  911.     add_word("produce", U2, 3, VOFFSET(Utype, produce));
  912.     add_word("productivity", U2, 3, VOFFSET(Utype, productivity));
  913.     add_word("storage", U2, 3, VOFFSET(Utype, storage));
  914.     add_word("consume", U2, 3, VOFFSET(Utype, consume));
  915.     add_word("in-length", U2, 3, VOFFSET(Utype, inlength));
  916.     add_word("out-length", U2, 3, VOFFSET(Utype, outlength));
  917.     add_word("survival", U1, 2, OFFSET(Utype, survival));
  918.     add_word("starve-message", S1, 2, OFFSET(Utype, starvemsg));
  919.     add_word("speed", U1, 2, OFFSET(Utype, speed));
  920.     add_word("moves", U2, 3, VOFFSET(Utype, moves));
  921.     add_word("random-move", U2, 3, VOFFSET(Utype, randommove));
  922.     add_word("free-move", U1, 2, OFFSET(Utype, freemove));
  923.     add_word("one-move", U1, 2, OFFSET(Utype, onemove));
  924.     add_word("jump-move", U1, 2, OFFSET(Utype, jumpmove));
  925.     add_word("to-move", U2, 3, VOFFSET(Utype, tomove));
  926.     add_word("capacity", U2, 3, VOFFSET(Utype, capacity));
  927.     add_word("hold-volume", U1, 2, OFFSET(Utype, holdvolume));
  928.     add_word("volume", U1, 2, OFFSET(Utype, volume));
  929.     add_word("enter-time", U2, 3, VOFFSET(Utype, entertime));
  930.     add_word("leave-time", U2, 3, VOFFSET(Utype, leavetime));
  931.     add_word("bridge", U2, 3, VOFFSET(Utype, bridge));
  932.     add_word("alter-mobility", U2, 3, VOFFSET(Utype, mobility));
  933.     add_word("see-best", U1, 2, OFFSET(Utype, seebest));
  934.     add_word("see-worst", U1, 2, OFFSET(Utype, seeworst));
  935.     add_word("see-range", U1, 2, OFFSET(Utype, seerange));
  936.     add_word("visibility", U1, 2, OFFSET(Utype, visibility));
  937.     add_word("conceal", U2, 3, VOFFSET(Utype, conceal));
  938.     add_word("always-seen", U1, 2, OFFSET(Utype, seealways));
  939.     add_word("all-seen", P0, 1, OFFSET(Period, allseen));
  940.     add_word("multi-part", U1, 2, OFFSET(Utype, parts));
  941.     add_word("hp", U1, 2, OFFSET(Utype, hp));
  942.     add_word("crippled", U1, 2, OFFSET(Utype, crippled));
  943.     add_word("hit", U2, 3, VOFFSET(Utype, hit));
  944.     add_word("hit-range", U1, 2, OFFSET(Utype, hitrange));
  945.     add_word("neutrality", P0, 1, OFFSET(Period, neutrality));
  946.     add_word("defense", U2, 3, VOFFSET(Utype, defense));
  947.     add_word("damage", U2, 3, VOFFSET(Utype, damage));
  948.     add_word("can-counter", U1, 2, OFFSET(Utype, counterable));
  949.     add_word("area-radius", U1, 2, OFFSET(Utype, arearadius));
  950.     add_word("nuke-hit", P0, 1, OFFSET(Period, nukehit));
  951.     add_word("self-destruct", U1, 2, OFFSET(Utype, selfdestruct));
  952.     add_word("combat-time", U1, 2, OFFSET(Utype, hittime));
  953.     add_word("counterattack", P0, 1, OFFSET(Period, counterattack));
  954.     add_word("capture", U2, 3, VOFFSET(Utype, capture));
  955.     add_word("guard", U2, 3, VOFFSET(Utype, guard));
  956.     add_word("protect", U2, 3, VOFFSET(Utype, protect));
  957.     add_word("changes-side", U1, 2, OFFSET(Utype, changeside));
  958.     add_word("retreat", U1, 2, OFFSET(Utype, retreat));
  959.     add_word("skill-effect", U1, 2, OFFSET(Utype, skillf));
  960.     add_word("discipline-effect", U1, 2, OFFSET(Utype, disciplinef));
  961.     add_word("morale-effect", U1, 2, OFFSET(Utype, moralef));
  962.     add_word("hits-with", U2, 3, VOFFSET(Utype, hitswith));
  963.     add_word("hit-by", U2, 3, VOFFSET(Utype, hitby));
  964.     add_word("destroy-message", S1, 2, OFFSET(Utype, destroymsg));
  965.     add_word("clear-side-names", FN, 0, clear_side_names);
  966.     add_word("clear-unit-names", FN, 0, clear_unit_names);
  967.     add_word("sname", FN, 1, add_side_name);
  968.     add_word("uname", FN, 1, add_unit_name);
  969.     add_word("begin{notes}", FN, 0, begin_notes);
  970.     add_word("print", FN, 0, print_stack);
  971.     add_word("end", FN, 0, NULL);
  972.     mustbenew = TRUE;
  973. }
  974.  
  975. /* Run a doublecheck on plausibility of period parameters.  Additional */
  976. /* checks are performed elsewhere as needed, for instance during random */
  977. /* generation.  Serious mistakes exit now, since they can cause all sorts */
  978. /* of strange behavior and core dumps.  It's a little more friendly to only */
  979. /* exit at the end of the tests, so all the mistakes can be found at once. */
  980.  
  981. check_period_numbers()
  982. {
  983.     bool failed = FALSE, movers = FALSE, makers = FALSE;
  984.     int u1, u2, r1, r2, t1, t2;
  985.  
  986.     if (period.numutypes < 1) {
  987.     fprintf(stderr, "No units have been defined at all!\n");
  988.     exit(1);
  989.     }
  990.     if (period.numttypes < 1) {
  991.     fprintf(stderr, "No terrain types have been defined at all!\n");
  992.     exit(1);
  993.     }
  994.     if (!between(0, period.mindistance, period.maxdistance)) {
  995.     fprintf(stderr, "Warning: Country distances %d to %d screwed up\n",
  996.         period.mindistance, period.maxdistance);
  997.     }
  998.     for_all_unit_types(u1) {
  999.     if (utypes[u1].hp <= 0) {
  1000.         fprintf(stderr, "Utype %d has nonpositive hp!\n", u1);
  1001.         failed = TRUE;
  1002.     }
  1003.     if (utypes[u1].speed > 0) {
  1004.         movers = TRUE;
  1005.     }
  1006.     for_all_unit_types(u2) {
  1007.         if (utypes[u1].make[u2] > 0) {
  1008.         if (utypes[u1].maker) makers = TRUE;
  1009.         if (!could_carry(u1, u2) && !could_carry(u2, u1)) {
  1010.             fprintf(stderr,
  1011.                 "Utype %d cannot hold product %d or vice versa!\n",
  1012.                 u1, u2);
  1013.             failed = TRUE;
  1014.         }
  1015.         }
  1016.     }
  1017.     }
  1018.     for_all_terrain_types(t1) {
  1019.     for_all_terrain_types(t2) {
  1020.         if (t1 != t2 && ttypes[t1].tchar == ttypes[t2].tchar) {
  1021.         fprintf(stderr, "Terrain types %d and %d both use '%c'!\n",
  1022.             t1, t2, ttypes[t1].tchar);
  1023.         failed = TRUE;
  1024.         }
  1025.     }
  1026.     }
  1027.     if (!movers && !makers) {
  1028.     fprintf(stderr, "No movers or builders have been defined!\n");
  1029.     failed = TRUE;
  1030.     }
  1031.     if (failed) exit(1);
  1032.     if (Debug) {
  1033.     printf("\n    %d unit types, %d resource types, %d terrain types\n",
  1034.            period.numutypes, period.numrtypes, period.numttypes);
  1035.     }
  1036. }
  1037.